﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Utility.Math;
using UnityEditor;

namespace Utility.NodeGraphs
{
	[NodeEditor (typeof (TreeNode))]
	public class TreeNodeEditor : NodeEditor
	{
		public virtual float GetHeaderHeight (Node node) => 20;
		public virtual RectOffset GetContentMargin (Node node) => new RectOffset (3, 3, 6, 6);
		public virtual Vector2 GetPortSize (Node node) => new Vector2 (12, 12);
		public virtual Vector2 GetContentSize (Node node) => new Vector2 (120, 0);
		public virtual float LinePadding => 2;

		public virtual bool ShowAddChildPortButton (Node node) => true;
		public virtual bool PortCanStartConnectionByLeftClick (Node node, int portIndex) => portIndex != 0;

		public override Vector2 GetSize (Node node)
		{
			TreeNode treeNode = (TreeNode)node;

			float h_header = GetHeaderHeight (node);
			Vector2 size_content = GetContentSize (node);
			Vector2 size_port = GetPortSize (node);
			RectOffset margin_content = GetContentMargin (node);

			bool showAddBtn = ShowAddChildPortButton (node) && node.CanAddPort ();

			//width
			float w = size_content.x + size_port.x + margin_content.horizontal;

			//height
			float h = h_header + margin_content.vertical;

			float h_left = size_port.y;
			float h_right = size_port.y * (treeNode.ChildPortCount + (showAddBtn ? 1 : 0));
			float h_content = size_content.y;
			h += Mathf.Max (h_left, h_right, h_content);

			return new Vector2 (w, h);
		}


		public override Rect GetDraggableRect (Rect nodeRect, Node node)
		{
			return nodeRect.ResizeHeight (GetHeaderHeight (node));
		}

		public override void DrawNodeBody (Rect nodeRect, Node node)
		{
			var size_port = GetPortSize (node);
			nodeRect = nodeRect.ShrinkWidth (size_port.x);
			var margin = GetContentMargin (node);

			Rect rect_header = nodeRect.ResizeHeight (GetHeaderHeight (node));
			DrawHeader (rect_header, node);

			Rect rect_body = nodeRect.ShrinkDown (GetHeaderHeight (node) - 3);
			GUI.Box (rect_body, "");

			Rect rect_content = rect_body.Shrink (margin).ShrinkWidth (size_port.x);
			DrawContents (rect_content, node);
		}

		protected virtual void DrawHeader (Rect headerRect, Node node)
		{
			GUI.Box (headerRect, node.name, GUI.skin.button);
		}

		protected virtual void DrawContents (Rect contentRect, Node node) { }

		public override void DrawPorts (Rect nodeRect, Node node)
		{
			float h_header = GetHeaderHeight (node);
			Vector2 size_port = GetPortSize (node);
			var margin = GetContentMargin (node);

			Rect rect_nodebody = nodeRect.ShrinkWidth (size_port.x).ShrinkDown (h_header).ShrinkHeight (margin.bottom, margin.top);

			TreeNode treeNode = (TreeNode)node;

			//parent
			if (!treeNode.isRoot)
			{
				var port = treeNode.ParentPort;
				if (port != null)
				{
					port.rect = rect_nodebody.Resize (size_port).MoveCenterTo (rect_nodebody.xMin, rect_nodebody.center.y);
					if (GUI.Button (port.rect, ""))
					{
						switch (Event.current.button)
						{
							case 0:
								if (PortCanStartConnectionByLeftClick (node, port.Index))
								{
									GetWindow (node.graph).StartConnect (port);
								}
								break;
							case 1:
								OpenPortContextMenu (port);
								break;
						}
					}
				}
			}

			//child
			{
				bool showAddBtn = ShowAddChildPortButton (node) && node.CanAddPort ();
				int count = treeNode.ChildPortCount + (showAddBtn ? 1 : 0);
				if (count > 0)
				{
					float padding = (rect_nodebody.height - count * size_port.y) / count;
					Rect rect_port = rect_nodebody.Resize (size_port).MoveCenterTo (rect_nodebody.xMax, rect_nodebody.yMin + padding / 2 + size_port.y / 2);
					foreach (var port in treeNode.ChildPorts)
					{
						port.rect = rect_port;
						//change dir point to make curve look better at close range
						if (port.OutputCount > 0)
						{
							var targetPort = port.outputs[0].TargetPort;
							float dx = targetPort.CanvasRect.x - port.CanvasRect.x;
							float d = (dx > 100 || dx < 0) ? 50 : dx / 2;
							var newOutput = port.outputs[0];
							newOutput.outDirPoint = Vector2.right * d;
							newOutput.inDirPoint = Vector2.left * d;
							port.outputs[0] = newOutput;
						}
						if (GUI.Button (port.rect, ""))
						{
							switch (Event.current.button)
							{
								case 0:
									if (PortCanStartConnectionByLeftClick (node, port.Index))
									{
										GetWindow (node.graph).StartConnect (port);
									}
									break;
								case 1:
									OpenPortContextMenu (port);
									break;
							}
						}
						rect_port = rect_port.ShiftUp ().ShiftUp (padding);
					}
					//draw add port button
					if (showAddBtn)
					{
						if (GUI.Button (rect_port, ""))
						{
							if (Event.current.button == 0)
							{
								node.AddNewPort (0, 1, Vector2.right * 50);
							}
						}
						GUI.DrawTexture (rect_port.Shrink (rect_port.width / 2, rect_port.width / 2), GUI.skin.GetStyle ("ShurikenPlus").normal.background);
					}
				}
			}
		}

		#region menu
		protected override GenericMenu BuildNodeContextMenu (Node node)
		{
			GenericMenu menu = new GenericMenu ();
			AddMenuItem_RemoveNode (menu, node);
			return menu;
		}

		protected override GenericMenu BuildPortContextMenu (NodePort port)
		{
			GenericMenu menu = new GenericMenu ();
			if (port.outputLimit != 0)
			{
				AddMenuItem_InsertPortAbove (menu, port, 0, 1, Vector2.right * 50);
				AddMenuItem_InsertPortBelow (menu, port, 0, 1, Vector2.right * 50);
				AddMenuItem_RemovePort (menu, port);
			}
			AddMenuItem_RemoveConnections (menu, port);
			return menu;
		}
		#endregion
	}
}